﻿using JT1078.Gateway.Extensions;
using JT1078.Gateway.Jobs;
using JT808.Protocol;
using JT808.Protocol.Extensions.JT1078.MessageBody;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using ZY.VICS.RabbitMQ;
using ZY.VICS.RabbitMQ.PubsubMode;
using ExchangeType = ZY.VICS.RabbitMQ.PubsubMode.ExchangeType;

namespace JT1078.Gateway;

public interface ITerminalDataHandler
{
    Task HandlerAsync(string key, ReadOnlyMemory<byte> bytes);
}
public class TerminalDataHandler : ITerminalDataHandler
{
    public ILogger Logger { get; set; }
    private readonly JT808Serializer _jt808Serializer;
    private readonly ConcurrentDictionary<string, List<TempPackage>> tempPackage = new ConcurrentDictionary<string, List<TempPackage>>();
    private MediaQueryCompleteCacheManager QueryCompleteCache { get; }
    public TerminalDataHandler(ILogger<TerminalDataHandler> logger, IJT808Config jt808config, MediaQueryCompleteCacheManager queryCompleteCache)
    {
        Logger = logger;
        _jt808Serializer = jt808config.GetSerializer();
        QueryCompleteCache = queryCompleteCache;
    }

    public Task HandlerAsync(string key, ReadOnlyMemory<byte> bytes)
    {
        return Task.Run(() =>
        {
            try
            {
                OnHandler(key, bytes);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, $"消费处理终端数据时发生错误:key:{key} bytes:{Convert.ToHexString(bytes.ToArray())}");
            }
        });
    }

    private void OnHandler(string key, ReadOnlyMemory<byte> bytes)
    {
        var span = bytes.Span;

        // 发布端发布的数据
        //uint headerValue = BinaryPrimitives.ReadUInt32BigEndian(span.Slice(4, 4));
        uint headerValue2 = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(4, 4));

        //_logger.LogInformation("ReaderPackage2 [{0}] {1} {2}", headerValue, headerValue2, Convert.ToHexString(span.ToArray()));

  
        //if (headerValue == 0x80000020) //实时位置信息
        //{
        //    return;  
        //}
        if (headerValue2 == 0x60000051) //终端其他消息下行
        {
            var msgType = span.Slice(12 + 4, 1).ToArray()[0];
            if (msgType == 0)
            {
                var msgType2 = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(12 + 9 + 1, 2));
                if (msgType2 == 0x1205) //终端上传音视频资源列表数据格式
                {
                    try
                    {
                        On0x1205(key, span, headerValue2, headerValue2);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex, "0x60000051_0x1205:");
                    }

                }
                else if(msgType2 == 0x0104) //查询终端参数应答
                {
                    //try
                    //{
                    //    On0x0104(key, span, headerValue, headerValue2);
                    //}
                    //catch (Exception ex)
                    //{
                    //    Logger.LogError(ex, "0x60000051_0x1205:");
                    //}
                }
                else if (msgType2 == 0x1003) //查询终端音视频属性应答
                {
                    try
                    {
                        On0x1003(key, span, headerValue2, headerValue2);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex, "0x60000051_0x1003:");
                    }
                }
            }
        }
    }

    /// <summary>
    /// 查询终端音视频参数
    /// </summary>
    /// <param name="key"></param>
    /// <param name="span"></param>
    /// <param name="headerValue"></param>
    /// <param name="headerValue2"></param>
    private void On0x1003(string key, ReadOnlySpan<byte> span, uint headerValue, uint headerValue2)
    {
        var rrr = span.Slice(12 + 9).ToArray();
        Logger.LogInformation("0x60000051_0x1003 [{0} {1} {2}]", headerValue, headerValue2, Convert.ToHexString(rrr));
         
    }
 
    private void On0x1205(string key, ReadOnlySpan<byte> span, uint headerValue, uint headerValue2)
    {
        var rrr = span.Slice(12 + 9).ToArray();
        Logger.LogInformation("0x60000051_0x1205 [{0} {1} {2}]", headerValue, headerValue2, Convert.ToHexString(rrr));

        JT808Package jT808_0X0801_1 = _jt808Serializer.Deserialize<JT808Package>(rrr);
        var isPackage = jT808_0X0801_1.Header.MessageBodyProperty.IsPackage; //分包
        if (isPackage) 
        {
            if (jT808_0X0801_1.Header.PackageIndex == jT808_0X0801_1.Header.PackgeCount)
            {
                tempPackage.Remove(key, out List<TempPackage> list);
                list = (list ?? new List<TempPackage>()).OrderBy(c => c.Index).ToList();
                //var list = tempPackage.GetValueOrDefault(key) ?? new List<TempPackage>();
                var list2 = new List<byte>();
                foreach (var item in list)
                {
                    list2.AddRange(item.Body);
                }
                list2.AddRange(jT808_0X0801_1.SubDataBodies);


                var jT808_0x1205 = _jt808Serializer.Deserialize<JT808_0x1205>(list2.ToList().ToArray());

                var videoOrderAck = new VideoOrderAck();
                videoOrderAck.Status = 1;
                videoOrderAck.VideoList = new JTVideoListInfo()
                {
                    FileCount = jT808_0x1205.AVResouceTotal,
                    SerialNumber = jT808_0x1205.MsgNum,
                    FileList = jT808_0x1205.AVResouces.Select(x => new JTVideoFileListItem()
                    {
                        Alarm = x.AlarmFlag,
                        Channel = x.LogicChannelNo,
                        EndTime = x.EndTime,
                        FileSize = x.FileSize,
                        MediaType = x.AVResourceType,
                        StartTime = x.BeginTime,
                        StorageType = x.MemoryType,
                        StreamType = x.StreamType
                    }).ToList()
                };



                QueryCompleteCache.Set(jT808_0x1205.MsgNum.ToString(), new MediaQueryCompleteCacheItem() { SimNo = key, Order = videoOrderAck }); 

                //new RedisHelper().StringSet("OCX_ORDERINFO_" + jT808_0x1205.MsgNum + key, videoOrderAck, new TimeSpan(0, 10, 0));
            }
            else
            {
                if (jT808_0X0801_1.Header.PackageIndex == 1)
                {
                    tempPackage.Remove(key, out _); //清除上一次数据包断包
                }

                var list = tempPackage.GetValueOrDefault(key) ?? new List<TempPackage>();
                list.Add(new TempPackage()
                {
                    Index = jT808_0X0801_1.Header.PackageIndex,
                    Count = jT808_0X0801_1.Header.PackgeCount,
                    Body = jT808_0X0801_1.SubDataBodies
                });
                tempPackage.AddOrUpdate(key, list.OrderBy(c => c.Index).ToList());
            }
        }
        else
        {
            var videoOrderAck = new VideoOrderAck();
            videoOrderAck.Status = 1;

            // 包长度21表示没有数据上传 1 + 2 + 2 + 6 + 2 + (2 + 4) + 1 + 1 
            if (rrr.Length <= 21)
            {
                var fileCount = BinaryPrimitives.ReadUInt32BigEndian(span.Slice(21 + 15, 4));
                var serialNumber = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(21 + 13, 2));
                videoOrderAck.VideoList = new JTVideoListInfo()
                {
                    FileCount = fileCount,
                    SerialNumber = serialNumber,
                    FileList = new List<JTVideoFileListItem>()
                };

                QueryCompleteCache.Set(serialNumber.ToString(), new MediaQueryCompleteCacheItem() { SimNo = key, Order = videoOrderAck });
                //new RedisHelper().StringSet("OCX_ORDERINFO_" + videoOrderAck.VideoList.SerialNumber + key, videoOrderAck, new TimeSpan(0, 10, 0));
                return;
            }
            else
            {
                var jT808_0x1205 = _jt808Serializer.Deserialize<JT808_0x1205>(rrr);


                videoOrderAck.VideoList = new JTVideoListInfo()
                {
                    FileCount = jT808_0x1205.AVResouceTotal,
                    SerialNumber = jT808_0x1205.MsgNum,
                    FileList = jT808_0x1205.AVResouces.Select(x => new JTVideoFileListItem()
                    {
                        Alarm = x.AlarmFlag,
                        Channel = x.LogicChannelNo,
                        EndTime = x.EndTime,
                        FileSize = x.FileSize,
                        MediaType = x.AVResourceType,
                        StartTime = x.BeginTime,
                        StorageType = x.MemoryType,
                        StreamType = x.StreamType
                    }).ToList()
                };

                QueryCompleteCache.Set(jT808_0x1205.MsgNum.ToString(), new MediaQueryCompleteCacheItem() { SimNo = key, Order = videoOrderAck });
                //new RedisHelper().StringSet("OCX_ORDERINFO_" + jT808_0x1205.MsgNum + key, videoOrderAck, new TimeSpan(0, 10, 0));
            }
        }
    }

    protected class TempPackage
    {
        public DateTime time;
        public TempPackage()
        {
            time = DateTime.Now;
        }

        public int Index { get; set; }
        public int Count { get; set; }

        public byte[] Body { get; set; }
    }
}

public class TerminalDataSubscribeWorker : IHostedService
{
    protected ISubFactory SubFactory { get; }
    protected ISub TerminalDataSub { get; private set; }

    protected AsyncTimer Timer { get; }

    protected readonly ILogger<TerminalDataSubscribeWorker> _logger;
    protected List<string> TerminalNo = new List<string>();
    private ITerminalDataHandler TerminalDataHandler { get; set; }

    public TerminalDataSubscribeWorker(ISubFactory subFactory,
        ITerminalDataHandler terminalDataHandler,
        AsyncTimer timer,
        ILogger<TerminalDataSubscribeWorker> logger)
    {
        SubFactory = subFactory;
        Timer = timer;
        _logger = logger;
        TerminalDataHandler = terminalDataHandler;

        Timer.Period = 60 * 1000;
        Timer.Elapsed = Timer_Elapsed;
        Timer.RunOnStart = true;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("start ...");

        TerminalDataSub = SubFactory.Create(new ExchangeDeclareConfiguration("TerminalData", ExchangeType.Direct),
            consumer: new ConsumerConfiguration() { AutoAck = true }
            );
        TerminalDataSub.OnMessageReceived(ProcessTerminalDataAsync);

        await TerminalDataSub.BindAsync("541250000012");
        Timer.Start(cancellationToken);

    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("stop ...");

        foreach (var item in TerminalNo)
        {
            await TerminalDataSub.UnbindAsync(item);
        }

        Timer.Stop(cancellationToken);
    }

    protected virtual async Task Timer_Elapsed(AsyncTimer timer)
    {
        // 不更新
        await Task.CompletedTask;
    }

    private async Task ProcessTerminalDataAsync(IModel channel, BasicDeliverEventArgs ea)
    {
        await TerminalDataHandler.HandlerAsync(ea.RoutingKey, ea.Body);
        //_logger.LogDebug($"terminal msg: {ea.RoutingKey} {System.Text.Encoding.UTF8.GetString(ea.Body.ToArray())}");
    }

}

/// <summary>
/// 视频指令应答消息
/// </summary>
public class VideoOrderAck
{
    /// <summary>
    /// 返回值状态:0(初始)，1（成功）,2（设备不在线），3（失败），4（等待回应超时），5（等待回应中），6（作废）
    /// </summary>
    public int Status { get; set; }

    /// <summary>
    /// 录像列表
    /// </summary>
    public JTVideoListInfo VideoList { get; set; }

    /// <summary>
    /// 错误消息
    /// </summary>
    public string ErrMessage { get; set; }
}

/// <summary>
/// 终端上传音视频资源列表数据格式
/// （0x1205）
/// </summary>
[DataContract]
public class JTVideoListInfo
{
    /// <summary>
    /// 流水号（对应查询音视频资源列表指令的流水号）
    /// </summary>
    [DataMember]
    public UInt16 SerialNumber { get; set; }
    /// <summary>
    /// 音视频资源总数（无符合条件的音视频资源，置为0）
    /// </summary>
    [DataMember]
    public UInt32 FileCount { get; set; }
    /// <summary>
    /// 音视频资源列表（见表23）
    /// </summary>
    [DataMember]
    public List<JTVideoFileListItem> FileList { get; set; }

}

/// <summary>
/// 终端上传音视频资源列表数据格式
/// 终端上传音视频资源列表格式
/// （0x1205）
/// </summary>
[DataContract]
public class JTVideoFileListItem
{
    /// <summary>
    /// 逻辑通道号（0表示所有通道）
    /// </summary>
    [DataMember]
    public byte Channel { get; set; }
    /// <summary>
    /// 开始时间（yyyy-MM-dd HH:mm:ss）
    /// </summary>
    [DataMember]
    public DateTime StartTime { get; set; }
    /// <summary>
    /// 结束时间（yyyy-MM-dd HH:mm:ss）
    /// </summary>
    [DataMember]
    public DateTime EndTime { get; set; }
    /// <summary>
    /// 报警标志（bit0~bit31见JT/T 808-2011表18报警标志位定义；bit32~bit64见表13；全0表示无报警类型条件）
    /// </summary>
    [DataMember]
    public UInt64 Alarm { get; set; }
    /// <summary>
    /// 音视频资源类型（0：音视频，1：音频，2：视频，3：视频或音视频）
    /// </summary>
    [DataMember]
    public byte MediaType { get; set; }
    /// <summary>
    /// 码流类型（0：所有码流，1：主码流，2：子码流）
    /// </summary>
    [DataMember]
    public byte StreamType { get; set; }
    /// <summary>
    /// 存储器类型（0：所有存储器，1：主存储器，2：灾备存储器）
    /// </summary>
    [DataMember]
    public byte StorageType { get; set; }
    /// <summary>
    /// 文件大小（单字节BYTE）
    /// </summary>
    [DataMember]
    public uint FileSize { get; set; }

}

[Serializable]
public class VehicleAlarmDto
{
    public DateTime T { get; set; }
    /// <summary>
    /// 报警信息，多个报警之间用"/"分割
    /// </summary>
    public string A { get; set; }
}




public class BaseEventArgs : EventArgs
{
    public string Id { get; set; } = string.Empty;

    public ReadOnlyMemory<byte> Bytes { get; set; }
}

